using System;
using System.Collections.Generic;
using System.ComponentModel;
using Microsoft.Ccr.Core;
using Microsoft.Dss.Core.Attributes;
using Microsoft.Dss.ServiceModel.Dssp;
using Microsoft.Dss.Core.DsspHttp;
using Microsoft.Dss.ServiceModel.DsspServiceBase;
using W3C.Soap;
using submgr = Microsoft.Dss.Services.SubscriptionManager;
using AForge.Video;
using AForge.Vision;
using AForge.Robotics.Surveyor;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using webcam = Microsoft.Robotics.Services.WebCam.Proxy;
using SRV1.Properties;


namespace SRV1.Camera
{
    [Contract(Contract.Identifier)]
    [DisplayName("SRV1 Camera Service")]
    [Description("New SRV1 Camera service")]
    class SRV1Service : DsspServiceBase
    {
        /// <summary>
        /// Service state
        /// </summary>
        [ServiceState]
        SRV1State _state = new SRV1State();

        /// <summary>
        /// Main service port
        /// </summary>
        [ServicePort("/SRV1", AllowMultipleInstances = true)]
        SRV1Operations _mainPort = new SRV1Operations();

        SRV1Camera camera = null;

        private static AForgeNetController.AForgeNetController _srv1 = new AForgeNetController.AForgeNetController();

        [SubscriptionManagerPartner]
        submgr.SubscriptionManagerPort _submgrPort = new submgr.SubscriptionManagerPort();

        /// <summary>
        /// Alternate service port
        /// </summary>
        [AlternateServicePort(AlternateContract = webcam.Contract.Identifier)]
        webcam.WebCamOperations _webCamPort = new webcam.WebCamOperations();

        // New Frame Port
        private Port<NewFrameEventArgs> _newFramePort = null;

        /// <summary>
        /// Service constructor
        /// </summary>
        public SRV1Service(DsspServiceCreationPort creationPort)
            : base(creationPort)
        {
            _newFramePort = new Port<NewFrameEventArgs>();

            if (_srv1.IsConnected != true)
            {
                _srv1.Connect(Settings.Default.IpAddress);
            }
            camera = _srv1.GetCamera(SVS.Camera.Left);
            camera.FrameInterval = 400;
            camera.NewFrame += new NewFrameEventHandler(camera_NewFrame);
            camera.Start();
        }

        private void camera_NewFrame(object sender, NewFrameEventArgs eventArgs)
        {
            _newFramePort.Post(eventArgs);
        }

        private void NewFrameNotification(NewFrameEventArgs eventArgs)
        {
            webcam.UpdateFrame updateFrame = new webcam.UpdateFrame();

            updateFrame.Body.TimeStamp = DateTime.Now;
            SendNotification<webcam.UpdateFrame>(_submgrPort, updateFrame);
            _webCamPort.Post(updateFrame);
            updateFrame.ResponsePort.Post(DefaultUpdateResponseType.Instance);
        }

        private byte[] ConvertBitmap(Bitmap bitmap)
        {
            BitmapData raw = null;
            byte[] rawImage = null;

            try
            {
                raw = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                                        ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

                int size = raw.Height * raw.Stride;
                rawImage = new byte[size];

                System.Runtime.InteropServices.Marshal.Copy(raw.Scan0, rawImage, 0, size);
            }
            finally
            {
                if (raw != null)
                {
                    bitmap.UnlockBits(raw);
                }
            }

            return rawImage;
        }

        /// <summary>
        /// Service start
        /// </summary>
        protected override void Start()
        {

            // 
            // Add service specific initialization here
            // 

            base.Start();

            base.MainPortInterleave.CombineWith(
                Arbiter.Interleave(
                                new TeardownReceiverGroup(),
                                new ExclusiveReceiverGroup
                                (
                                    Arbiter.Receive(true, _newFramePort, NewFrameNotification)
                                ),
                                new ConcurrentReceiverGroup()
                                    )
                                );
        }

        /// <summary>
        /// Handles UpdateFrame requests on alternate port WebCam
        /// </summary>
        /// <param name="updateframe">request message</param>
        [ServiceHandler(PortFieldName = "_webCamPort")]
        [ServiceHandler(ServiceHandlerBehavior.Exclusive)]
        public virtual IEnumerator<ITask> WebCamUpdateFrameHandler(webcam.UpdateFrame updateframe)
        {
            updateframe.Body.TimeStamp = DateTime.Now;
            updateframe.ResponsePort.Post(DefaultUpdateResponseType.Instance);

            SendNotification(_submgrPort, updateframe);

            yield break;
        }

        /// <summary>
        /// Handles QueryFrame requests on alternate port WebCam
        /// </summary>
        /// <param name="queryframe">request message</param>
        [ServiceHandler(PortFieldName = "_webCamPort")]
        [ServiceHandler(ServiceHandlerBehavior.Exclusive)]
        public virtual IEnumerator<ITask> WebCamQueryFrameHandler(webcam.QueryFrame queryframe)
        {
            Bitmap bitmapImage = _srv1.GetLeftImage();

            byte[] imageBytes = ConvertBitmap(bitmapImage);

            webcam.QueryFrameResponse response = new webcam.QueryFrameResponse();
            response.Frame = imageBytes;
            response.Size = bitmapImage.Size;
            response.TimeStamp = DateTime.Now;

            queryframe.ResponsePort.Post(response);

            yield break;
        }

        /// <summary>
        /// Handles Get requests on alternate port WebCam
        /// </summary>
        /// <param name="get">request message</param>
        [ServiceHandler(PortFieldName = "_webCamPort")]
        public virtual IEnumerator<ITask> WebCamGetHandler(webcam.Get get)
        {
            get.ResponsePort.Post((webcam.WebCamState)_state.Clone());

            yield break;
        }

        /// <summary>
        /// Handles HttpGet requests on alternate port WebCam
        /// </summary>
        /// <param name="httpget">request message</param>
        [ServiceHandler(PortFieldName = "_webCamPort")]
        public void WebCamHttpGetHandler(Microsoft.Dss.Core.DsspHttp.HttpGet httpget)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Handles HttpQuery requests on alternate port WebCam
        /// </summary>
        /// <param name="httpquery">request message</param>
        [ServiceHandler(PortFieldName = "_webCamPort")]
        public void WebCamHttpQueryHandler(Microsoft.Dss.Core.DsspHttp.HttpQuery httpquery)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Handles Replace requests on alternate port WebCam
        /// </summary>
        /// <param name="replace">request message</param>
        [ServiceHandler(PortFieldName = "_webCamPort")]
        [ServiceHandler(ServiceHandlerBehavior.Concurrent)]
        public virtual IEnumerator<ITask> WebCamReplaceHandler(webcam.Replace replace)
        {
            replace.Body.CopyTo(_state);

            replace.ResponsePort.Post(DefaultReplaceResponseType.Instance);

            yield break;
        }

        /// <summary>
        /// Handles Subscribe requests on alternate port WebCam
        /// </summary>
        /// <param name="subscribe">request message</param>
        [ServiceHandler(PortFieldName = "_webCamPort")]
        [ServiceHandler(ServiceHandlerBehavior.Concurrent)]
        public virtual IEnumerator<ITask> WebCamSubscribeHandler(webcam.Subscribe subscribe)
        {
            LogInfo("Webcam Subscription from: " + subscribe.Body.Subscriber);
            SubscribeHelper(_submgrPort, subscribe.Body, subscribe.ResponsePort);

            yield break;
        }
    }
}


